/**
 ******************************************************************************
 * @file           : bsp_iic_gpio.c
 * @brief          : Software I2C Driver Implementation (STM32F4xx)
 ******************************************************************************
 * @attention
 *
 * 软件模拟I2C驱动实现文件
 * 完整实现I2C协议时序
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/IIC_GPIO/bsp_iic_gpio.h"
#include "./BSP/IIC_GPIO/bsp_iic_gpio_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static volatile IIC_GPIO_Error_e sg_iic_gpio_last_error = IIC_GPIO_OK;  /*!< IIC_GPIO最后一次错误 */
/* Private function prototypes -----------------------------------------------*/
static void iic_gpio_delay(void);
static void iic_gpio_sda_out(void);
static void iic_gpio_sda_in(void);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       I2C延时函数
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        调整延时可改变I2C速度
 */
static void iic_gpio_delay(void)
{
    volatile uint32_t i = IIC_GPIO_DELAY_COUNT;
    while (i--)
    {
        __NOP();
    }
}

/**
 * @brief       SDA设置为输出模式
 *
 * @param       无
 *
 * @retval      无
 */
static void iic_gpio_sda_out(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};

    gpio_init_struct.Pin = IIC_GPIO_SDA_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;        /* 开漏输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 高速 */
    HAL_GPIO_Init(IIC_GPIO_SDA_PORT, &gpio_init_struct);
}

/**
 * @brief       SDA设置为输入模式
 *
 * @param       无
 *
 * @retval      无
 */
static void iic_gpio_sda_in(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};

    gpio_init_struct.Pin = IIC_GPIO_SDA_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;            /* 输入模式 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(IIC_GPIO_SDA_PORT, &gpio_init_struct);
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       I2C初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        初始化SCL和SDA引脚
 */
void iic_gpio_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};

    /* 使能GPIO时钟 */
    IIC_GPIO_SCL_CLK_ENABLE();
    IIC_GPIO_SDA_CLK_ENABLE();

    /* 配置SCL引脚（开漏输出） */
    gpio_init_struct.Pin = IIC_GPIO_SCL_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(IIC_GPIO_SCL_PORT, &gpio_init_struct);

    /* 配置SDA引脚（开漏输出） */
    gpio_init_struct.Pin = IIC_GPIO_SDA_PIN;
    HAL_GPIO_Init(IIC_GPIO_SDA_PORT, &gpio_init_struct);

    /* 总线空闲状态 */
    IIC_SCL_HIGH();
    IIC_SDA_HIGH();

    log_i("I2C GPIO初始化完成");
}

/**
 * @brief       产生I2C起始信号
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        SCL高电平时，SDA下降沿
 */
void iic_gpio_start(void)
{
    iic_gpio_sda_out();     /* SDA输出模式 */
    
    IIC_SDA_HIGH();
    IIC_SCL_HIGH();
    iic_gpio_delay();
    
    IIC_SDA_LOW();          /* START信号：SDA下降沿 */
    iic_gpio_delay();
    
    IIC_SCL_LOW();          /* 钳住总线，准备发送数据 */
    iic_gpio_delay();
}

/**
 * @brief       产生I2C停止信号
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        SCL高电平时，SDA上升沿
 */
void iic_gpio_stop(void)
{
    iic_gpio_sda_out();     /* SDA输出模式 */
    
    IIC_SCL_LOW();
    IIC_SDA_LOW();
    iic_gpio_delay();
    
    IIC_SCL_HIGH();
    iic_gpio_delay();
    
    IIC_SDA_HIGH();         /* STOP信号：SDA上升沿 */
    iic_gpio_delay();
}

/**
 * @brief       发送ACK应答
 *
 * @param       无
 *
 * @retval      无
 */
void iic_gpio_send_ack(void)
{
    iic_gpio_sda_out();
    
    IIC_SCL_LOW();
    IIC_SDA_LOW();          /* ACK：SDA拉低 */
    iic_gpio_delay();
    
    IIC_SCL_HIGH();
    iic_gpio_delay();
    
    IIC_SCL_LOW();
    iic_gpio_delay();
}

/**
 * @brief       发送NACK非应答
 *
 * @param       无
 *
 * @retval      无
 */
void iic_gpio_send_nack(void)
{
    iic_gpio_sda_out();
    
    IIC_SCL_LOW();
    IIC_SDA_HIGH();         /* NACK：SDA保持高 */
    iic_gpio_delay();
    
    IIC_SCL_HIGH();
    iic_gpio_delay();
    
    IIC_SCL_LOW();
    iic_gpio_delay();
}

/**
 * @brief       等待ACK应答
 *
 * @param       无
 *
 * @retval      IIC_ACK/IIC_NACK
 *
 * @note        返回IIC_ACK表示收到应答
 */
IIC_Ack_e iic_gpio_wait_ack(void)
{
    uint8_t timeout = 0;
    IIC_Ack_e ack;
    
    iic_gpio_sda_in();      /* SDA输入模式 */
    
    IIC_SDA_HIGH();
    iic_gpio_delay();
    
    IIC_SCL_HIGH();
    iic_gpio_delay();
    
    /* 读取SDA状态 */
    while (IIC_SDA_READ())
    {
        timeout++;
        if (timeout > 250)
        {
            iic_gpio_stop();
            return IIC_NACK;
        }
    }
    
    ack = IIC_ACK;
    
    IIC_SCL_LOW();
    iic_gpio_delay();
    
    return ack;
}

/**
 * @brief       发送一个字节
 *
 * @param[in]   data 要发送的字节
 *
 * @retval      无
 *
 * @note        高位在前
 */
void iic_gpio_send_byte(uint8_t data)
{
    uint8_t i;
    
    iic_gpio_sda_out();
    IIC_SCL_LOW();
    
    for (i = 0; i < 8; i++)
    {
        if (data & 0x80)
        {
            IIC_SDA_HIGH();
        }
        else
        {
            IIC_SDA_LOW();
        }
        
        data <<= 1;
        iic_gpio_delay();
        
        IIC_SCL_HIGH();
        iic_gpio_delay();
        IIC_SCL_LOW();
        iic_gpio_delay();
    }
}

/**
 * @brief       读取一个字节
 *
 * @param[in]   ack 是否发送ACK（IIC_ACK/IIC_NACK）
 *
 * @retval      读取的字节
 */
uint8_t iic_gpio_read_byte(IIC_Ack_e ack)
{
    uint8_t i;
    uint8_t data = 0;
    
    iic_gpio_sda_in();      /* SDA输入模式 */
    
    for (i = 0; i < 8; i++)
    {
        IIC_SCL_LOW();
        iic_gpio_delay();
        
        IIC_SCL_HIGH();
        iic_gpio_delay();
        
        data <<= 1;
        
        if (IIC_SDA_READ())
        {
            data |= 0x01;
        }
    }
    
    IIC_SCL_LOW();
    
    /* 发送应答或非应答 */
    if (ack == IIC_ACK)
    {
        iic_gpio_send_ack();
    }
    else
    {
        iic_gpio_send_nack();
    }
    
    return data;
}

/**
 * @brief       写单个寄存器
 *
 * @param[in]   dev_addr 设备地址（7位地址<<1）
 * @param[in]   reg_addr 寄存器地址
 * @param[in]   data 要写入的数据
 *
 * @retval      0=成功，1=失败
 */
uint8_t iic_gpio_write_reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
    iic_gpio_start();
    
    iic_gpio_send_byte(dev_addr);       /* 发送设备地址+写 */
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_send_byte(reg_addr);       /* 发送寄存器地址 */
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_send_byte(data);           /* 发送数据 */
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_stop();
    
    return 0;
}

/**
 * @brief       读单个寄存器
 *
 * @param[in]   dev_addr 设备地址（7位地址<<1）
 * @param[in]   reg_addr 寄存器地址
 * @param[out]  data 读取的数据
 *
 * @retval      0=成功，1=失败
 */
uint8_t iic_gpio_read_reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data)
{
    iic_gpio_start();
    
    iic_gpio_send_byte(dev_addr);       /* 发送设备地址+写 */
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_send_byte(reg_addr);       /* 发送寄存器地址 */
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_start();                   /* 重复起始 */
    
    iic_gpio_send_byte(dev_addr | 0x01); /* 发送设备地址+读 */
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    *data = iic_gpio_read_byte(IIC_NACK); /* 读取数据并发送NACK */
    
    iic_gpio_stop();
    
    return 0;
}

/**
 * @brief       写多个字节
 *
 * @param[in]   dev_addr 设备地址（7位地址<<1）
 * @param[in]   reg_addr 寄存器地址
 * @param[in]   buf 要写入的数据缓冲区
 * @param[in]   len 数据长度
 *
 * @retval      0=成功，1=失败
 */
uint8_t iic_gpio_write_bytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t *buf, uint16_t len)
{
    uint16_t i;
    
    iic_gpio_start();
    
    iic_gpio_send_byte(dev_addr);
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_send_byte(reg_addr);
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    for (i = 0; i < len; i++)
    {
        iic_gpio_send_byte(buf[i]);
        if (iic_gpio_wait_ack() != IIC_ACK)
        {
            iic_gpio_stop();
            return 1;
        }
    }
    
    iic_gpio_stop();
    
    return 0;
}

/**
 * @brief       读多个字节
 *
 * @param[in]   dev_addr 设备地址（7位地址<<1）
 * @param[in]   reg_addr 寄存器地址
 * @param[out]  buf 读取的数据缓冲区
 * @param[in]   len 数据长度
 *
 * @retval      0=成功，1=失败
 */
uint8_t iic_gpio_read_bytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t *buf, uint16_t len)
{
    uint16_t i;
    
    iic_gpio_start();
    
    iic_gpio_send_byte(dev_addr);
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_send_byte(reg_addr);
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    iic_gpio_start();                   /* 重复起始 */
    
    iic_gpio_send_byte(dev_addr | 0x01);
    if (iic_gpio_wait_ack() != IIC_ACK)
    {
        iic_gpio_stop();
        return 1;
    }
    
    for (i = 0; i < len; i++)
    {
        if (i == len - 1)
        {
            buf[i] = iic_gpio_read_byte(IIC_NACK);  /* 最后一个字节发送NACK */
        }
        else
        {
            buf[i] = iic_gpio_read_byte(IIC_ACK);
        }
    }
    
    iic_gpio_stop();
    
    return 0;
}

/**
 * @brief       检测I2C设备是否存在
 *
 * @param[in]   dev_addr 设备地址（7位地址<<1）
 *
 * @retval      0=存在，1=不存在
 */
uint8_t iic_gpio_check_device(uint8_t dev_addr)
{
    uint8_t result;
    
    iic_gpio_start();
    
    iic_gpio_send_byte(dev_addr);
    result = (iic_gpio_wait_ack() == IIC_ACK) ? 0 : 1;
    
    iic_gpio_stop();
    
    log_d("检测I2C设备 0x%02X: %s", dev_addr, result ? "不存在" : "存在");
    
    return result;
}

/**
 * @brief       获取IIC_GPIO最后一次错误
 *
 * @param       无
 *
 * @retval      IIC_GPIO错误码
 */
IIC_GPIO_Error_e iic_gpio_get_last_error(void)
{
    return sg_iic_gpio_last_error;
}

/**
 * @brief       打印IIC_GPIO状态（调试接口）
 *
 * @param       无
 *
 * @retval      无
 */
void iic_gpio_print_status(void)
{
    const char *error_str[] = {
        "IIC_GPIO_OK",
        "IIC_GPIO_ERROR_PARAM",
        "IIC_GPIO_ERROR_TIMEOUT",
        "IIC_GPIO_ERROR_NACK",
        "IIC_GPIO_ERROR_BUS"
    };
    
    if (sg_iic_gpio_last_error < 5)
    {
        log_i("IIC_GPIO Last Error: %s", error_str[sg_iic_gpio_last_error]);
    }
    else
    {
        log_i("IIC_GPIO Last Error: UNKNOWN");
    }
}
